package algocity.modelo.necesidades;

import algocity.modelo.juego.DineroInsuficienteException;
import algocity.modelo.juego.JuegoNoIniciadoException;
import algocity.modelo.juego.Monetizable;
import algocity.modelo.mapa.Hectarea;
import algocity.modelo.zona.ConstruccionInvalidaExcepcion;
import algocity.modelo.zona.Ruptura;

import java.io.Serializable;
import java.util.Iterator;

import static java.lang.Math.abs;

public abstract class Conexion
        extends Ruptura
        implements Serializable
{

    protected int costoDeConstruccionPorHectarea;
    private Hectarea origen;
    private Hectarea destino;
    private Monetizable monedero;

    public Conexion(Monetizable monedero) {
        this.monedero = monedero;
        this.reparacionSegunConstruccion = 0;
    }

    public int calcularCosto() {
        return (longitudHectareasX() + longitudHectareasY()) * getCostoDeConstruccionPorHectarea();
    }

    private int longitudHectareasY() {
        return abs( origen.getPosicion().getY() - destino.getPosicion().getY());
    }

    private int longitudHectareasX() {
        return abs( origen.getPosicion().getX() - destino.getPosicion().getX());
    }

    // ToDo suena mal la excepción NoHayJuegoIniciado, no se relaciona con construir
    public void construite(Hectarea origen, Hectarea destino) throws SuperficieInvalidaException, JuegoNoIniciadoException, DineroInsuficienteException, ConstruccionInvalidaExcepcion {
        validarOrigen(origen);
        validarDestino(destino);

        this.origen = origen;
        this.destino = destino;

        final int costo = calcularCosto();

        try {
            monedero.quitarDinero(costo);
        } catch (DineroInsuficienteException e) {
            this.origen = null;
            this.destino = null;
            throw e;
        }

        origen.agregarConexion(this);
        destino.agregarConexion(this);
        agregadosDeHectarea(origen, destino);
        ubicacion = origen.getPosicion();

    }

    public boolean esLineaDeTension() {
        return false;
    }
    public boolean esRutaPavimentada() {
        return false;
    }
    public boolean esTuberiaDeAgua() {
        return false;
    }

    protected abstract void validarOrigen(Hectarea hectarea) throws SuperficieInvalidaException, ConstruccionInvalidaExcepcion;

    protected abstract void validarDestino(Hectarea hectarea) throws SuperficieInvalidaException, ConstruccionInvalidaExcepcion;

    protected abstract void agregadosDeHectarea(Hectarea origen, Hectarea destino);

    @Override
    public void destruite() {
        destruirEnElExtermo(origen);
        destruirEnElExtermo(destino);
    }

    private void destruirEnElExtermo(Hectarea hectarea) {
        Iterator<Conexion> conexionesDisponibles = hectarea.getConexionesDisponibles().iterator();

        while( conexionesDisponibles.hasNext() ) {
            if (conexionesDisponibles.next() == this) {
                conexionesDisponibles.remove();
                return;
            }
        }
    }

    public int getCostoDeConstruccionPorHectarea() {
        return costoDeConstruccionPorHectarea;
    }

    public Hectarea getOrigen() {
        return origen;
    }

    public Hectarea getDestino() {
        return destino;
    }


    public boolean equals(Object otroObjeto)
    {
        if (!(otroObjeto instanceof LineaDeTension))
        {
            return super.equals(otroObjeto);
        }

        if (otroObjeto == this)
        {
            return true;
        }

        Conexion otro = (Conexion) otroObjeto;
        return (costoDeConstruccionPorHectarea == otro.costoDeConstruccionPorHectarea) &&
                // No se pueden comparar hectáreas porque da StackOverflow, entonces se compararan
                // sólo las posiciones que es lo que importa en este caso
                (
                        (origen == null && otro.origen == null) ||
                        (origen != null && otro.origen != null && origen.getPosicion().equals(otro.origen.getPosicion()))
                ) &&
                (
                        (destino == null && otro.destino == null) ||
                        (destino != null && otro.destino != null && destino.getPosicion().equals(otro.destino.getPosicion()))
                ) &&
                ( monedero.getDinero() == otro.monedero.getDinero() )  && // FIXME issue #19
                super.equals(otro);

    }
}
